from pydantic import BaseModel, RootModel, Field, validator
from typing import List, Dict, Any, Optional, Union
from src.core import retMes


def ensure_dict(obj: Union[BaseModel, Dict[str, Any]]) -> Dict[str, Any]:
    """
    通用工具：将 Pydantic 模型或 dict 统一转成 dict
================================================
- 场景：接口返回、日志记录、数据落库前统一格式。
- 注意：仅接受 BaseModel 或 dict，其余类型会抛 TypeError。
    """
    if isinstance(obj, BaseModel):
        return obj.dict(exclude_unset=True)
    if isinstance(obj, dict):
        return obj
    retMes.Raise(message=f"无法处理对象 {obj}").mes()
    return None


class ResponseModel(BaseModel):
    """
    统一响应体 ResponseModel
    =====================
    设计目标：让前端/调用方拿到结构一致的返回包，便于统一错误处理与数据消费。

    字段说明
    --------
    status_code : int
        业务级状态码，区别于 HTTP 状态码。推荐 0=成功，非 0=各类错误。
    status : str
        简短状态文案，如 "success"、"fail"、"partial_success"。
    message : str
        给人看的详细描述，可直接弹窗或写入日志。
    record_count : Optional[int]
        查询类接口返回总记录数，方便前端分页。非查询场景可省略。
    data : Union[Dict[str, Any], List[Any]]
        实际载荷，支持单条对象或列表。若接口无数据可返回 {} 或 []。
        历史改动：早期仅支持 List[Dict]，后放宽为 dict 或 list，以减少冗余包装。
    """
    status_code: int
    status: str
    message: str
    record_count: Optional[int] = None
    data: Union[Dict[str, Any], List[Any]] = []  # 或 = {}
    # data: dict  # List[Dict[str, Any]]          # ← 关键改动 # dict


class QueryModel(BaseModel):
    """
    查询参数 QueryModel
    =================
    - 提供分页与通用过滤能力，可被子类扩展。
    - 额外过滤字段放在 data: dict 中，避免在 URL 里拼长串。

    字段说明
    --------
    page : Optional[int] = 1
        页码，从 1 开始计数。
    page_size : Optional[int] = 100
        每页条数，默认 100；后端可做上限保护。
    data : Optional[dict] = None
        其余过滤条件 kv 结构，例如 {"status": "active", "region": "CN"}。
    """
    page: Optional[int] = 1
    page_size: Optional[int] = 100
    data: Optional[dict] = None  # 其他过滤字段
    sort: Optional[List[str]] = None


class UpdateItem(BaseModel):
    pk_names: List[str] = None
    pk_values: Optional[List[str]] = None
    data: Optional[Dict[str, Any]] = None

    class Config:
        extra = "forbid"  # 禁止多余字段


class UpdateModel(RootModel[List[UpdateItem]]):
    """
        批量更新请求 UpdateModel（含 UpdateItem）
        =====================================
        - 支持一次调用更新多条记录，主键可复合（pk_names + pk_values）。
        - 采用 RootModel[List[UpdateItem]] 的扁平结构，方便前端直接 JSON 数组请求。

        UpdateItem 字段说明
        -------------------
        pk_names : List[str]
            主键字段名列表，顺序与 pk_values 一一对应。
        pk_values : Optional[List[str]]
            主键值列表，长度必须与 pk_names 一致。
        data : Optional[Dict[str, Any]]
            待更新的字段值，仅包含发生变化的键值对即可。
        Config.extra = "forbid"
            禁止多余字段，避免客户端误传导致 SQL 注入或字段覆盖。

        使用示例
        --------
        POST /batch_update
        [
          {
            "pk_names": ["id"],
            "pk_values": ["123"],
            "data": {"status": "disabled", "updated_by": "admin"}
          },
          {
            "pk_names": ["tenant_id", "sku"],
            "pk_values": ["T1", "ABC"],
            "data": {"price": 99}
          }
        ]
        """
    pass


class DeleteItem(BaseModel):
    deletion_model: str = Field("delete", description='"delete" 硬删；"mark" 软删')
    pk_names: List[str] = None
    pk_values: Optional[List[str]] = None
    deletion_reason: str = None
    data: Optional[Dict[str, Any]] = None

    class Config:
        extra = "forbid"  # 禁止多余字段

    """    
    # 1. 主键字段与值必须成对出现
    @validator("pk_values")
    def check_pk_consistency(cls, v, values):
        names = values.get("pk_names")
        if v and len(v) != len(names):
            raise ValueError("pk_names 和 pk_values 长度不一致")
        return v
    
    # 2. 软删必须填写 deletion_reason
    @validator("deletion_reason")
    def check_reason(cls, v, values):
        mode = values.get("deletion_Model")
        if mode == "mark" and (v is None or v.strip() == ""):
            raise ValueError("软删除模式下 deletion_reason 不能为空")
        return v
    """


class DeleteModel(RootModel[List[DeleteItem]]):
    """
        批量更新请求 DeleteModel（含 DeleteItem）
        =====================================
        - 支持一次调用更新多条记录，主键可复合（pk_names + pk_values）。
        - 采用 RootModel[List[DeleteItem]] 的扁平结构，方便前端直接 JSON 数组请求。

        DeleteItem 字段说明
        -------------------
        deletion_model:str
            删除模式，delete硬删除即直接删除，mark：软删除即表标注删除标志
        pk_names : List[str]
            主键字段名列表，顺序与 pk_values 一一对应。
        pk_values : Optional[List[str]]
            主键值列表，长度必须与 pk_names 一致。
        deletion_reason：str
            删除原因，软删除必须要填写删除原因，便于后期查询
        data : Optional[Dict[str, Any]]
            待更新的字段值，仅包含发生变化的键值对即可。
        Config.extra = "forbid"
            禁止多余字段，避免客户端误传导致 SQL 注入或字段覆盖。

        使用示例
        --------
        POST /batch_delete
        [
          {
            "deletion_model":"mark",
            "pk_names": ["id"],
            "pk_values": ["123"],
            "deletion_reason":"测试删除",
            "data": {}
          }
        ]
        """
    pass
